﻿using Microsoft.Extensions.Logging;
using Quickuse.Logging.FileLogger;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading;

namespace Quickuse.Logging.EsLogger
{
    /// <summary>
    /// Es 日志处理器
    /// </summary>
    internal class EsLoggerProcessor : IDisposable
    {
        private IEsLoggerSettings _esLoggerSettings;
        private readonly BlockingCollection<LogMessageEntry> _messageQueue;
        private readonly Thread _outputHttpThread;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="esLoggerSettings"></param>
        public EsLoggerProcessor(IEsLoggerSettings esLoggerSettings)
        {
            _esLoggerSettings = esLoggerSettings;
            _messageQueue = new BlockingCollection<LogMessageEntry>(PublicConst.MaxQueuedLength);

            _outputHttpThread = new Thread(ProcessLogQueue)
            {
                IsBackground = true,
                Name = "Es logger queue processing thread"
            };
            _outputHttpThread.Start();

            if (_esLoggerSettings.ChangeToken != null)
            {
                _esLoggerSettings.ChangeToken.RegisterChangeCallback(OnConfigurationReload, null);
            }
        }

        /// <summary>
        /// OnConfigurationReload
        /// </summary>
        /// <param name="state"></param>
        public void OnConfigurationReload(object state)
        {
            try
            {
                _esLoggerSettings = _esLoggerSettings.Reload();
            }
            catch (Exception ex)
            {
                InternalLog.Error($" while loading configuration changes.{Environment.NewLine}{ex}");
            }
            finally
            {
                // The token will change each time it reloads, so we need to register again.
                if (_esLoggerSettings?.ChangeToken != null)
                {
                    _esLoggerSettings.ChangeToken.RegisterChangeCallback(OnConfigurationReload, null);
                }
            }
        }

        /// <summary>
        /// 入队消息
        /// </summary>
        /// <param name="message">message</param>
        /// <param name="logLevel">logLevel</param>
        internal void EnqueueMessage(string message, LogLevel logLevel)
        {
            if (_esLoggerSettings.IsAsyn && _messageQueue.IsAddingCompleted)
            {
                _messageQueue.Add(new LogMessageEntry { Message = message, logLevel = logLevel });
            }
            else
            {
                SendHttp(message);
            }
        }

        /// <summary>
        /// ProcessLogQueue
        /// </summary>
        public void ProcessLogQueue()
        {
            try
            {
                List<string> logMessages = new List<string>();
                foreach (var message in _messageQueue.GetConsumingEnumerable())
                {
                    logMessages.Add(message.Message);
                }

                if (logMessages.Count > 0)
                {
                    SendHttp(logMessages);
                    logMessages.Clear();
                }
            }
            catch (Exception ex)
            {
                InternalLog.Error($" while ProcessLogQueue.{Environment.NewLine}{ex}");
            }
        }

        /// <summary>
        /// Send Es
        /// </summary>
        /// <param name="message">message</param>
        public void SendHttp(string message)
        {
            try
            {
                if (string.IsNullOrEmpty(message))
                {
                    return;
                }

                var esUrl = GetEsUrl();
                using (HttpClient httpClient = new HttpClient())
                {
                    httpClient.Timeout = TimeSpan.FromMilliseconds(_esLoggerSettings.Timeout);
                    using (StringContent httpContent = new StringContent(message, Encoding.UTF8, "application/json"))
                    {
                        httpClient.PostAsync(esUrl, httpContent).Wait();
                    }
                }
            }
            catch (Exception ex)
            {
                InternalLog.Error($" while SendHttp.{Environment.NewLine}{ex}");
            }
        }

        /// <summary>
        /// Send Es
        /// </summary>
        /// <param name="logMessages">logMessages</param>
        public void SendHttp(List<string> logMessages)
        {
            try
            {
                if (string.IsNullOrEmpty(_esLoggerSettings.EsServer))
                {
                    return;
                }
                var esUrl = GetEsUrl();
                using (HttpClient httpClient = new HttpClient())
                {
                    httpClient.Timeout = TimeSpan.FromMilliseconds(_esLoggerSettings.Timeout);
                    var logMessagepage = logMessages.Paging(1000);
                    foreach (var logpage in logMessagepage)
                    {
                        var text = "{ \"index\":{}}\r\n" + string.Join("\r\n{ \"index\":{}}\r\n", logpage) + "\r\n";
                        using (StringContent httpContent = new StringContent(text, Encoding.UTF8, "application/json"))
                        {
                            httpClient.PostAsync(esUrl + "/_bulk", httpContent).Wait();
                        }
                    }
                    logMessages.Clear();
                }
            }
            catch (Exception ex)
            {
                InternalLog.Error($" while SendHttp.{Environment.NewLine}{ex}");
            }
        }

        /// <summary>
        /// Get Es Url
        /// </summary>
        /// <returns></returns>
        private string GetEsUrl()
        {
            try
            {
                if (string.IsNullOrEmpty(_esLoggerSettings.IndexNameDtFormat))
                {
                    return $"{_esLoggerSettings.EsServer}/{_esLoggerSettings.IndexName}/{_esLoggerSettings.TypeName}";
                }
                else
                {
                    return $"{_esLoggerSettings.EsServer}/{_esLoggerSettings.IndexName}-{DateTime.Now.ToString(_esLoggerSettings.IndexNameDtFormat)}/{_esLoggerSettings.TypeName}";
                }
            }
            catch
            {
                return $"{_esLoggerSettings.EsServer}/{_esLoggerSettings.IndexName}/{_esLoggerSettings.TypeName}";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {

        }
    }
}
